package queue

import "sync"
import "context"

type ConcurrentBlockingQueue[T any] struct {
	mutex    *sync.Mutex
	data     []T
	notFull  *sync.Cond
	notEmpty *sync.Cond
}

func NewConcurrentBlockingQueue[T any](maxSize int) *ConcurrentBlockingQueue[T] {
	m := &sync.Mutex{}
	return &ConcurrentBlockingQueue[T]{
		data:     make([]T, 0, maxSize),
		mutex:    m,
		notFull:  sync.NewCond(m),
		notEmpty: sync.NewCond(m),
	}
}

func (c *ConcurrentBlockingQueue[T]) EnQueue(ctx context.Context, data T) error {
	if ctx.Err() != nil {
		return ctx.Err()
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	if c.IsFull() {
		// 我阻塞住我自己，直到有人唤醒我
		c.notFull.Wait()
	}
	// 1. 扩容问题，出队并没能缓解问题。维护两个指针
	c.data = append(c.data, data)
	c.notEmpty.Signal()
	return nil
}

func (c *ConcurrentBlockingQueue[T]) DeQueue(ctx context.Context) (T, error) {
	if ctx.Err() != nil {
		var t T
		return t, ctx.Err()
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	for c.IsEmpty() {
		// 阻塞住我自己，等待有元素入队
		// 一睡不醒，超时也不知道
		c.notEmpty.Wait()
	}
	t := c.data[0]
	c.data = c.data[1:]
	c.notFull.Signal()
	return t, nil
}

func (c *ConcurrentBlockingQueue[T]) IsFull() bool {
	panic("implement me")
}

func (c *ConcurrentBlockingQueue[T]) IsEmpty() bool {
	return len(c.data) == 0
}

func (c *ConcurrentBlockingQueue[T]) Len() uint64 {
	return uint64(len(c.data))
}
